﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using System.Xml;
using System.Xml.Linq;

namespace ShootThemUp
{
    public class PlayingState : GameState
    {
        public const int NB_NIVEAU = 3;
        Player joueur;
        List<Tir> tirs = new List<Tir>();
        List<Enemy> enemies = new List<Enemy>();
        Texture2D textureTir;
        Texture2D textureBonus;
        Texture2D textureViePleine;
        Texture2D textureVieVide;
        Texture2D textureVieSegment;

        // Fond
        Texture2D textureBackground;
        int positionBackground = 0;
        int positionCadreBackground = 2048 - 700;

        //Texture2D textureEnemy;
        //Dictionary<EnumTypeEnnemi, Texture2D> texturesEnemy;
        TimeSpan tempDepart;
        bool isTempsInit = false;
        Dictionary<EnumTypeEnnemi, TypeEnnemi> typesEnnemi = new Dictionary<EnumTypeEnnemi, TypeEnnemi>();
        List<Bonus> lsBonus = new List<Bonus>();
        List<Apparition> apparitions = new List<Apparition>();
        SpriteFont FontScore;

        TimeSpan tempsGetReady = new TimeSpan(0, 0, 3);
        TimeSpan tempsGoodJob = new TimeSpan(0, 0, 5);

        TimeSpan tempsFinLevel;

        bool LevelTermine = false;

        int NumeroNiveau;

        public PlayingState()
        {
            NumeroNiveau = 1;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization logic here
        }

        private void ChargeApparitions()
        {
            apparitions.Clear();
            if (NumeroNiveau > NB_NIVEAU)
            {
                StateChange("Menu");
                return;
            }
            XmlDocument level = new XmlDocument();
            level.Load("Levels\\" + NumeroNiveau + ".xml");

            foreach (XmlElement element in level["level"]["apparitionsEnnemis"].ChildNodes)
            {
                Apparition apparition = new Apparition(int.Parse(element.Attributes["moment"].Value));

                foreach (XmlElement xmlEnemi in element.ChildNodes)
                {
                    EnumTypeEnnemi type = (EnumTypeEnnemi)Enum.Parse(typeof(EnumTypeEnnemi), xmlEnemi.Attributes["type"].Value, true);


                    Enemy enemi = new Enemy(new Vector2(float.Parse(xmlEnemi.Attributes["x"].Value), 5), typesEnnemi[type]);

                    apparition.enemies.Add(enemi);
                }

                apparitions.Add(apparition);
            }
        }

        public override void Load(ContentManager content)
        {
            joueur = new Player(content.Load<Texture2D>("joueur"), new Point(5, 0));

            textureViePleine = content.Load<Texture2D>("viePleine");
            textureVieVide = content.Load<Texture2D>("vieVide");
            textureVieSegment = content.Load<Texture2D>("vieSegment");

            textureBackground = content.Load<Texture2D>("background");

            textureTir = content.Load<Texture2D>("tir");
            //textureEnemy = content.Load<Texture2D>("enemy");
            FontScore = content.Load<SpriteFont>("ScoreFont");

            Dictionary<EnumTypeEnnemi, Texture2D> texturesEnemy = new Dictionary<EnumTypeEnnemi, Texture2D>();
            texturesEnemy.Add(EnumTypeEnnemi.Vaisseau, content.Load<Texture2D>("ennemi2"));
            texturesEnemy.Add(EnumTypeEnnemi.Frite, content.Load<Texture2D>("boss"));
            texturesEnemy.Add(EnumTypeEnnemi.Haricot, content.Load<Texture2D>("miniboss1"));
            texturesEnemy.Add(EnumTypeEnnemi.Patate, content.Load<Texture2D>("ennemi1"));
            texturesEnemy.Add(EnumTypeEnnemi.Boss, content.Load<Texture2D>("enn1"));

            textureBonus = content.Load<Texture2D>("bonus");
            // Chargement des types d'ennemis
            XElement rootEnnemis = XElement.Load("TypesEnnemis.xml");
            foreach (XElement element in rootEnnemis.Descendants("type"))
            {
                TypeEnnemi typeEnnemi = new TypeEnnemi(element);
                typeEnnemi.Texture = texturesEnemy[typeEnnemi.Type];
                typesEnnemi.Add(typeEnnemi.Type, typeEnnemi);
            }

            ChargeApparitions();
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        public override void Unload()
        {

        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            //Recupération bonus
            for (int i = 0; i < lsBonus.Count; i++)
            {
                if (Controleur.estDansCadre(joueur.Rectangle, lsBonus[i].Rectangle))
                {
                    joueur.LsBonus.Add(lsBonus[i]);
                    lsBonus[i] = null;
                    lsBonus.RemoveAt(i);
                    i--;
                }
            }

            //initialisation du temps
            if (!isTempsInit)
            {
                tempDepart = gameTime.TotalGameTime;
                isTempsInit = true;
            }


            foreach (Apparition apparition in apparitions.Where(a => !a.Termine))
            {
                if (apparition.Moment < gameTime.TotalGameTime - tempDepart)
                {
                    foreach (Enemy enemy in apparition.enemies)
                    {
                        enemies.Add(enemy);
                        Enemy.DateDernierEnemy = new TimeSpan();
                    }

                    apparition.Termine = true;
                }
            }


            for (int i = 0; i < enemies.Count; i++)
            {
                bool breaking = false;
                enemies[i].update(gameTime);
                if (enemies[i].EstSupprimable)
                {
                    enemies[i] = null;
                    enemies.RemoveAt(i);
                    i--;
                }
                else
                {
                    if (tirs != null && tirs.Count != 0)
                        for (int j = 0; j < tirs.Count; j++)
                            if (Controleur.estDansCadre(enemies[i].Rectangle, tirs[j].Rectangle))
                            {
                                 enemies[i].NbVies--;

                                if (enemies[i].NbVies == 0)
                                {
                                    TypeBonus tBonus = enemies[i].Destruction();
                                    if (tBonus != TypeBonus.Rien)
                                    {
                                        Bonus bonus = new Bonus(enemies[i].Position, textureBonus, new Point(0,7));
                                        lsBonus.Add(bonus);
                                    }
                                    enemies[i] = null;
                                    enemies.RemoveAt(i);

                                    joueur.Vie += 100;
                                    i--;
                                    breaking = true;
                                }

                                tirs[j] = null;
                                tirs.RemoveAt(j);

                                break;
                            }
                    if (breaking) break;
                    if (joueur != null)
                        if (Controleur.estDansCadre(enemies[i].Rectangle, joueur.Rectangle))
                        {
                            enemies[i] = null;
                            enemies.RemoveAt(i);
                            joueur.Vie -= Player.DAMAGE;
                            if (joueur.Vie <= 0)
                            {
                                //game over
                                StateChange("Menu");
                            }
                            i--;
                            break;
                        }
                }
            }

            // Allows the game to exit
            if (Keyboard.GetState().GetPressedKeys().Contains(Keys.Escape))
                this.Finalize();
            // TODO: Add your update logic here
            if (Keyboard.GetState().GetPressedKeys().Contains(Keys.Left))
                joueur.goGauche();
            if (Keyboard.GetState().GetPressedKeys().Contains(Keys.Right))
                joueur.goDroite();
            if (Keyboard.GetState().GetPressedKeys().Contains(Keys.Space))
            {
                tirs.AddRange(joueur.tire(textureTir, gameTime));
            }

            if (Keyboard.GetState().IsKeyUp(Keys.Space))
            {
                Controleur.DateDernierTir = Controleur.LIMITE_TIR;
            }


            for (int i = 0; i < tirs.Count; i++)
            {
                tirs[i].update();
                if (tirs[i].EstSupprimable)
                {
                    tirs[i] = null;
                    tirs.RemoveAt(i);
                    i--;
                }
            }

            foreach (Bonus bonus in lsBonus)
            {
                bonus.update(gameTime);
            }

            if (!LevelTermine && apparitions.Where(app => !app.Termine).Count() == 0 && enemies.Count == 0)
            {
                LevelTermine = true;
                tempsFinLevel = gameTime.TotalGameTime;
            }

            // Fond
            positionCadreBackground -= 1;

            if (positionCadreBackground <= -700) positionCadreBackground = 2000 - 700;
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(SpriteBatch sb, GameTime gt)
        {
            // TODO: Add your drawing code here
            sb.Begin();

            // Dessin du fond
            sb.Draw(textureBackground, new Vector2(0, -positionCadreBackground), new Rectangle(0, 0, 500, 2048), Color.White);
            sb.Draw(textureBackground, new Vector2(0, -positionCadreBackground - 2048), new Rectangle(0, 0, 500, 2048), Color.White);

            if (gt.TotalGameTime - tempDepart < tempsGetReady)
            {
                sb.DrawString(FontScore, "Level " + NumeroNiveau, new Vector2(200, 200), Color.Black);
                sb.DrawString(FontScore, "GET READY !!!!", new Vector2(250, 250), Color.Black);
            }

            DrawScore(sb);
            joueur.Draw(sb);
            foreach (Enemy enem in enemies)
                enem.Draw(sb);
            foreach (Tir tir in tirs)
                tir.Draw(sb);
            foreach (Bonus bonus in lsBonus)
                bonus.Draw(sb);

            if (LevelTermine)
            {
                if (gt.TotalGameTime - tempsFinLevel < tempsGoodJob)
                {
                    if (NumeroNiveau == NB_NIVEAU)
                        sb.DrawString(FontScore, "!!! GAME FINISHED !!!", new Vector2(130, 200), Color.Black);
                    else
                        sb.DrawString(FontScore, "GOOD JOB !!!", new Vector2(250, 250), Color.Black);
                }
                else
                {
                    LevelTermine = false;
                    tempDepart = gt.TotalGameTime;
                    NumeroNiveau++;
                    ChargeApparitions();
                }
            }

            sb.End();
        }

        public override void Finalize()
        {
            //throw new NotImplementedException();
        }

        private void DrawScore(SpriteBatch sb)
        {
            string vie = "VIE: ";
            Vector2 positionScore = new Vector2(5, 5);
            sb.DrawString(FontScore, vie, positionScore, Color.Black);

            int y = 15;
            for (int i = 1; i <= Player.NBVIESMAX; i++)
            {
                sb.Draw(textureVieVide, new Vector2(60 * i, y), Color.White);
            }
            int nbVie = joueur.Vie / Player.DAMAGE;
            int reste = (joueur.Vie % Player.DAMAGE) / 100;
            for (int i = 1; i <= nbVie; i++)
            {
                sb.Draw(textureViePleine, new Vector2(60 * i, y), Color.White);
            }
            for (int i = 0; i < reste; i++)
            {
                sb.Draw(textureVieSegment, new Vector2(60 * (nbVie + 1) + i * textureVieSegment.Width, y), Color.White);
            }
        }
    }
}
